#include "server.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>

#include <boost/program_options.hpp>
#include <boost/bind.hpp>

namespace mms
{

	Server::Server(const std::string &filename, int port)
	{
		_filename = filename;
		_port = port;
		_acceptor = new tcp::acceptor(_io_service, tcp::endpoint(tcp::v4(), _port));
	}

	Server::~Server()
	{
		delete _acceptor;
	}

	int Server::port() const
	{
		return _port;
	}

	std::string Server::filename() const
	{
		return _filename;
	}

	std::string Server::readRequest(tcp::socket &socket)
	{
		std::vector<char> got_data(8*1024);
		size_t length = socket.read_some(boost::asio::buffer(got_data));
		return std::string(got_data.begin(), got_data.end());
	}

	std::string Server::header(int contentLength)
	{
		std::string h =
		"HTTP/1.1 200 OK\r\n"
		"Content-Length: " + boost::lexical_cast<std::string>(contentLength) + "\r\n"
		"Content-Type: image/png;\r\n"
		"Connection: close\r\n"
		"\r\n";
		return h;
	}

	std::string Server::forbidden_header()
	{
		std::string h =
		"HTTP/1.1 403 Forbidden\r\n"
		"Connection: close\r\n"
		"\r\n";
		return h;
	}

	void Server::handle_accept(tcp::socket *socket)
	{
		std::string request = readRequest(*socket);

		std::string valid_req = "GET / HTTP/1.";
		bool is_valid = std::search(request.begin(),
			  request.end(),
			  valid_req.begin(),
			  valid_req.end()) != request.end();

		boost::system::error_code ignored_error;

		if (is_valid)
		{
		  std::ifstream file (_filename.c_str(), std::ios::in|std::ios::binary);

		  file.seekg(0, std::ios::end);
		  size_t size = file.tellg();
		  file.seekg(0, std::ios::beg);

		  boost::asio::write(*socket,
				  boost::asio::buffer(header(size)),
				  boost::asio::transfer_all(),
				  ignored_error);

		  char buf[8*1024];
		  while (file.read(buf, sizeof(buf)).gcount() > 0)
			  boost::asio::write(*socket,
					  boost::asio::buffer(buf),
					  boost::asio::transfer_all(),
					  ignored_error);
		}
		else
		  boost::asio::write(*socket,
					  boost::asio::buffer(forbidden_header()),
					  boost::asio::transfer_all(),
					  ignored_error);

		listen();
	}

	void Server::listen()
	{
		try
		{
			tcp::socket socket(_io_service);
			_acceptor->async_accept(
				  socket,
				  boost::bind(&Server::handle_accept, this, &socket)
			);

			_io_service.run();
		}
		catch (std::exception& e)
		{
			std::cerr << "exception: " << e.what() << std::endl;
			listen();
		}
	}

}
